package com.ruoyi.app.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.app.config.AppConfig;
import com.ruoyi.app.util.secure.SecureUtils;
import com.ruoyi.common.VersionUtils;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;

@Slf4j
public class ApiSecurityUtils {

    public static boolean securityVerify(ReqMsg reqMsg, AppConfig appConfig) throws Exception {
        if (StringUtils.isBlank(reqMsg.getReqId())
                // || (null == reqMsg.getData() || StringUtils.isBlank(reqMsg.getData().toString()))
                || StringUtils.isBlank(reqMsg.getSign())
                || StringUtils.isBlank(reqMsg.getRandom())
                || StringUtils.isBlank(reqMsg.getTimestamp())) {
            log.info("请求参数非空校验不通过");
            return false;
        }

        //获取安全性校验开关
        boolean mock = false;
        if (null != appConfig && "1".equals(appConfig.getSecurityMock())) {
            mock = true;
        }
        log.info("验签mock开关{}", mock);

        if (!mock) {
            StringBuilder signStr = new StringBuilder();
            signStr.append("appId=").append(reqMsg.getAppId() == null ? "" : reqMsg.getAppId()).append(Constants.SPLIT_STR);
            signStr.append("channeld=").append(reqMsg.getChanneld() == null ? "" : reqMsg.getChanneld()).append(Constants.SPLIT_STR);
            signStr.append("data=").append(reqMsg.getData() == null ? "" : reqMsg.getData()).append(Constants.SPLIT_STR);
            signStr.append("random=").append(reqMsg.getRandom() == null ? "" : reqMsg.getRandom()).append(Constants.SPLIT_STR);
            signStr.append("reqId=").append(reqMsg.getReqId() == null ? "" : reqMsg.getReqId()).append(Constants.SPLIT_STR);
            signStr.append("timestamp=").append(reqMsg.getTimestamp() == null ? "" : reqMsg.getTimestamp());
            String signParams = signStr.toString();
            //log.debug("签名串：{}", signParams);

            //签名校验用前端提供公钥
            boolean signRestut = SignUtil.signVerify(signParams.getBytes(), SignUtil.getMerchantPublicKey(appConfig.getSginPublickey()), Base64.decodeBase64(reqMsg.getSign()));
            if (!signRestut) {
                log.info("API调用安全性校验：签名验证不通过");
                return false;
            }

            //接口时效性校验
            String securityEffectiveTime = appConfig.getSecurityEffectiveTime();
            int effectiveTime = Integer.parseInt(securityEffectiveTime);
            if (effectiveTime > 0) {//有设置才比较       
                //请求时间
                Date requstTime = new Date();
                requstTime.setTime(Long.parseLong(reqMsg.getTimestamp()));

                //请求时间正确性校验
                Calendar newTime = Calendar.getInstance();
                newTime.setTime(new Date());
                newTime.add(Calendar.MINUTE, 1);//当前时间加两分钟允许误差1分钟内
                if (requstTime.after(newTime.getTime())) {//如果请求时间大于当前时间
                    log.info("API调用安全性校验：接口时间戳正确性校验不通过");
                    return false;
                }

                //请求时间超期校验
                Calendar reqTime = Calendar.getInstance();
                reqTime.setTime(requstTime);
                reqTime.add(Calendar.MINUTE, effectiveTime);//请求时间加上有效分钟
                if (reqTime.getTime().before(new Date())) {
                    log.info("API调用安全性校验：接口时效性校验不通过");
                    return false;
                }
            }
        }
        log.info("验证签名通过");
        return true;
    }

    public static ReqMsg decodeReq(ReqMsg reqMsg, String servicePrivatekey) throws Exception {
        String appVersion = ServletUtils.getRequest().getHeader("appVersion");
        log.info("appVersion:{}", appVersion);
        if (VersionUtils.compareVersion(appVersion, "2.2.2") < 0) {
            //随机数解密
            byte[] resultRandomByte = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(reqMsg.getRandom()), RsaUtil.getPrivateKey(servicePrivatekey));
            String resultRandom = new String(resultRandomByte);
            // log.info("对称AES随机数解密结果：{}", resultRandom);
            reqMsg.setRandom(resultRandom);
        }

        String resultBizJsonStr = null;

        // 解密
        log.info("开始解密：{}", reqMsg.toString());
        if (useNewSecurity(reqMsg)) {
            log.info("使用 3DES 解密");
            try {
                resultBizJsonStr = SecureUtils.decrypt3DES(reqMsg.getData().toString(), reqMsg.getRandom());
            } catch (Exception e) {
                log.error("----------3DES 解密异常 加密内容:" + reqMsg.getData().toString() + "------------随机数 :" + reqMsg.getRandom(), e);
            }
            log.info("业务数据解密结果[3DES]：{}", resultBizJsonStr);
        } else {
            resultBizJsonStr = new AesStrategy().decrypt(reqMsg.getData().toString(), reqMsg.getRandom());
            // String resultBizJsonStr = AesUtil.decrypt(Base64.decodeBase64(reqMsg.getData().toString()), resultRandom);
            // log.info("业务数据解密结果[O]：{}", resultBizJsonStr);
        }

        // log.info("业务数据解密结果：{}", resultBizJsonStr);
        if (resultBizJsonStr == null) {
            resultBizJsonStr = reqMsg.getParam();
        }
        //转json格式
        JSONObject dataJson = JSONObject.parseObject(resultBizJsonStr);
        reqMsg.setData(dataJson);

        return reqMsg;
    }

    private static boolean useNewSecurity(ReqMsg reqMsg) {
        return StringUtils.isNotBlank(reqMsg.getUseNewSecurity()) && StringUtils.equals("1", reqMsg.getUseNewSecurity());
    }

    public static RspMsg encodeReq(ReqMsg reqMsg, RspMsg rsp) throws Exception {
        //随机数解密
        //通过对称秘钥加密bizContent
        String dataStr = "";
        if (null != rsp.getData()) {
            if (rsp.getData() instanceof JSONObject) {
                JSONObject dataJson = (JSONObject) rsp.getData();
                dataStr = dataJson.toJSONString();
            } else if (rsp.getData() instanceof JSONArray) {
                JSONArray jsonArray = (JSONArray) rsp.getData();
                dataStr = jsonArray.toJSONString();
            } else if (rsp.getData() instanceof ArrayList || rsp.getData() instanceof LinkedList) {
                JSONArray jsonArray = JSON.parseArray(JSON.toJSONString(rsp.getData()));
                dataStr = jsonArray.toJSONString();
            } else {
                dataStr = JSON.toJSONString(rsp.getData());
            }
        }

        //加密
        log.info("开始加密：{},{}", dataStr, reqMsg.getRandom());

        String encryptBizcontent = null;
        if (useNewSecurity(reqMsg)) {
            encryptBizcontent = SecureUtils.encrypt3DES(dataStr, reqMsg.getRandom());
        } else {
            encryptBizcontent = new AesStrategy().encrypt(dataStr, reqMsg.getRandom());
            //String encryptBizcontent = Base64.encodeBase64String(AesUtil.encryptByPKCS5(dataStr.getBytes(), reqMsg.getRandom()));
        }

        rsp.setData(encryptBizcontent);

        return rsp;
    }

    public static RspMsg decodeRsp(RspMsg rsp, String random) throws Exception {
        if (rsp.getData() == null) {
            return rsp;
        }

        //随机数解密
        log.info("随机数解密：{},{}", rsp, random);
        String resultBizJsonStr = new AesStrategy().decrypt(rsp.getData().toString(), random);
        //String resultBizJsonStr = AesUtil.decrypt(Base64.decodeBase64(rsp.getData().toString()), random);
        log.info("业务数据解密结果：{}", resultBizJsonStr);

        //转json格式
        Object rspDataJson = null;
        try {
            rspDataJson = JSON.parseObject(resultBizJsonStr);
        } catch (Exception e) {
            rspDataJson = JSON.parseArray(resultBizJsonStr);
        }

        rsp.setData(rspDataJson);
        return rsp;
    }

    public static void main(String[] args) throws Exception {
        //加密
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("hasLogin", "0");
        jsonObject.put("creditRejectDays", "0");
        jsonObject.put("upToDays", "-1");
        log.info("开始加密：{},{}", jsonObject, "8926210777321690");
        String secretKey2 = AesUtil.getGcmSecretKey();
        try {
            String encryptBizcontent = new AesStrategy().encrypt(jsonObject.toString(), secretKey2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}